/*
 * Junger.java
 *
 * Copyright (c) 2006-2007 Computer-Aided Integrated Systems Laboratory, 
 * St. Petersburg Institute for Informatics and Automation RAS http://cais.lisa.iias.spb.su
 */

package guislicer;

import matching.GraphComparator;
import connectedsubgraph.ConnectedSubgraph;

import dbindex.ComparativeClass;

import Ontology.Classes;
import Ontology.Attributes;
import Ontology.ClassAttr;
import Ontology.WDHier;

import java.util.Vector;
import java.util.Map;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Set;
import java.util.List;

import edu.uci.ics.jung.utils.UserData;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.Vertex;
import edu.uci.ics.jung.graph.Edge;
import edu.uci.ics.jung.graph.impl.SparseGraph;
import edu.uci.ics.jung.graph.impl.SparseVertex;
import edu.uci.ics.jung.graph.impl.UndirectedSparseEdge;
import edu.uci.ics.jung.algorithms.shortestpath.UnweightedShortestPath;

/** Interface to jung.jar. Transform WebDeso ontology to jung graph and vice versa.
 */
public class Junger {
    
    /** Creates a new instance of Junger */
    public Junger() {
    }
    
    Graph graph;
    Map<Integer, Classes>   id_to_class;
    Map<Integer, Vertex>    class_id_to_vertex;
    Map<Vertex, Integer>    vertex_to_class_id;
    Map<Integer, Attributes> id_to_attr;
    
    /** Added Classes - Classes edges */
    Map<Integer, Integer> class_class;
    
    /** Map of subgraph vertices to vertices of the whole graph */
    Map<Vertex,Vertex> vert_subgraph_to_vert;
    /** Map of vertices of the whole graph to vertices of the subgraph */
    Map<Vertex,Vertex> vert_to_vert_subgraph;
    

        
    /** Returns true if map m contains pair (x,y) or (y,x) */
    public static boolean hasPair (Map<Integer, Integer> m, int x, int y) {
            
        if((m.containsKey(x) && y == m.get(x)) ||
           (m.containsKey(y) && x == m.get(y))) {
            return true;
        } else {
            return false;
        }
    }
                
    /** Creates jung Graph from WebDeso ontology. There is only one type of relation 
     * in form of edge. 
     * Between two vertices it will be created no more than 1 edge, even if there are more 
     * relations between vertices.
     */
    public Graph createGraph(Vector vClassAttr,Vector vClasses,Vector vAttributes,Vector vWDHier,Vector vWDAssoc,Vector vWDFunc,Vector vWDTax) {
    //public Graph createGraph(List vClassAttr,List vClasses,List vAttributes,List vWDHier,List vWDAssoc,List vWDFunc,List vWDTax) {
    //public Graph createGraph(Vector vClassAttr,Hashtable vClasses,Hashtable vAttributes,Vector vWDHier,Vector vWDAssoc,Vector vWDFunc,Vector vWDTax) {
    
        if(null == vClasses || 
                0 == vClasses.size() || 
                     !vClasses.get(0).getClass().getName().equalsIgnoreCase("Ontology.Classes"))
        {
            graph = null;
            return  null;
        }
        
        int         id1, id2;
        graph = new SparseGraph();
        
        id_to_class = new HashMap<Integer, Classes>();
        class_id_to_vertex = new HashMap<Integer, Vertex>();
        vertex_to_class_id = new HashMap<Vertex, Integer>();
        id_to_attr =  new HashMap<Integer, Attributes>();
        
        for(Object o:vClasses) {
            Ontology.Classes c = (Ontology.Classes)o;
            id_to_class.put(c.getClassID(), c);
            
            Vertex v = graph.addVertex(new SparseVertex());
            v.addUserDatum(GraphComparator.NAME_KEY, c.getClassName(), UserData.SHARED);
            class_id_to_vertex.put(c.getClassID(), v);
            vertex_to_class_id.put(v, c.getClassID());
        }
        /*for(Object o:vAttributes) {
            Attributes a = (Attributes)o;
            id_to_attr.put(a.getAttrID(), a);
        }*/
        
        // Adds Classes - Classes edges
        class_class = new HashMap<Integer, Integer>();    // map of added edges
        
        for(Object o:vClasses) {
            Classes c = (Classes)o;
            
            id1 = c.getClassID();
            id2 = c.getParentID();
            
            if(!Junger.hasPair(class_class, id1, id2))
            {
                Vertex v1 = class_id_to_vertex.get(id1);
                Vertex v2 = class_id_to_vertex.get(id2);
                if (null != v1 && null != v2) {
                    Edge e = graph.addEdge(new UndirectedSparseEdge(v1, v2));
                    class_class.put(id1, id2);
                }
            }
        }
        
        // Adds WDHier edges
        if(null != vWDHier) {
            for(Object o:vWDHier) {
                WDHier w = (WDHier)o;

                id1 = w.getClass1ID();
                id2 = w.getClass2ID();

                if(!Junger.hasPair(class_class, id1, id2))
                {
                    Vertex v1 = class_id_to_vertex.get(id1);
                    Vertex v2 = class_id_to_vertex.get(id2);
                    
                    if(null != v1 && null != v2) {
                        Edge e = graph.addEdge(new UndirectedSparseEdge(v1, v2));
                        class_class.put(id1, id2);
                    } else {
                        System.out.println("Warning in Junger.createGraph: vertex is null. v1="+v1+"; v2="+v2);
                    }
                }
            }
        }
        
        return graph;
    }
    
    
    /** Selects minimal subset of connected vertices (classes) by set of class_id 
     * (for one document) from index database. Set of class_id corresponds to 
     * the document (doc_id).
     *
     * It is possible that class_id are not connected directly in ontology. 
     * If they are connected via some vertices, then these vertices will be added 
     * to the result graph.
     *
     * @param g_all     the graph contains all ontology classes and relations
     * @param junger    contains mapping id_to_class, class_id_to_vertex, and vertex_to_class_id.
     * @param fn        filename, the graph in Pajek format to be stored to
     */
    public static Graph createGraphMinSubsetOfConnectedClasses(
            java.sql.Connection conn,int doc_id,int onto_id,
            Graph g_all, UnweightedShortestPath usp_all,
            Junger junger,
            String fn)
    {    
        int[] class_id = ComparativeClass.getClassIDWithNegativeWeight(conn, doc_id, onto_id);
        
        if(null == class_id || 0 == class_id.length) {
            return null;   
        }
        
        // initial (unconnected may be) subgraph
        Graph initial_subgraph = junger.getSubgraphByClassesID(class_id);
        
        return ConnectedSubgraph.selectsMinimumVerticesSetByLabelsCC(
                initial_subgraph,
                g_all, usp_all,
                junger.vert_subgraph_to_vert,
                junger.vert_to_vert_subgraph,
                fn);
    }
    
    
    /** Creates graph from index database by set of class_id (for one document).
     * It takes subgraph from graph g by doc_class_id.
     * <PRE>
     * Side effects: creates and fills:
     *  - map vert_subgraph_to_vert,
     *  - map vert_to_vert_subgraph</PRE>
     */
    public Graph getSubgraphByClassesID(int[] doc_class_id) {
        
        if(null == doc_class_id || 0 == doc_class_id.length) {
            return null;
        }
        
        // subgraph
        Graph g = new SparseGraph(); 
        Map<Integer, Vertex> class_id_to_vertex_subgraph = new HashMap<Integer, Vertex>();
        vert_subgraph_to_vert = new HashMap<Vertex,Vertex>();
        vert_to_vert_subgraph = new HashMap<Vertex,Vertex>();
        
        // add vertices
        for(int id:doc_class_id) {
            if(null != class_id_to_vertex &&
                       class_id_to_vertex.containsKey(id))
            {   
                //Classes c = id_to_class.get(id);
                Vertex v = class_id_to_vertex.get(id);
                Vertex v_sub = g.addVertex(new SparseVertex());
                //v_sub.addUserDatum(GraphComparator.NAME_KEY, c.getClassName(), UserData.SHARED);
                v_sub.addUserDatum(GraphComparator.NAME_KEY, v.getUserDatum(GraphComparator.NAME_KEY), UserData.SHARED);
                class_id_to_vertex_subgraph.put(id, v_sub);
                vert_subgraph_to_vert.put(v_sub,v);
                vert_to_vert_subgraph.put(v,v_sub);
            }
        }
        
        // add edges
        for(int id:doc_class_id) {
            if(null != class_id_to_vertex &&
                       class_id_to_vertex.containsKey(id))
            {
                Vertex v    = class_id_to_vertex         .get(id);
                Vertex v_sub= class_id_to_vertex_subgraph.get(id);
                
                for(Object o:v.getNeighbors()) {
                    int id_neigh = vertex_to_class_id.get((Vertex)o);
                    if(class_id_to_vertex_subgraph.containsKey(id_neigh)) {
                        Vertex v_sub_neigh = class_id_to_vertex_subgraph.get(id_neigh);
                        if(!v_sub.getNeighbors().contains(v_sub_neigh)) {
                            g.addEdge(new UndirectedSparseEdge(v_sub, v_sub_neigh));
                        }
                    }
                }
            }
        }
        
        return g;
    }
    
}


            
            
